package de.playmee.services;

import java.util.ArrayList;
import java.util.List;

import de.playmee.Playmee;
import de.playmee.Track;
import de.playmee.Track.Status;
import de.playmee.handler.AccountHandler;
import de.playmee.handler.MusicHandler;
import de.playmee.enums.Result;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

/**
 * Service that handles uploading and downloading data
 */
public class LoadingService extends ExtendedService {
	
	private final static String TAG = "LoadingService";
	
	// intent actions that can be handled by this service
	// defined by the <action> tags in the <intent-filters> tag in the AndroidManifest.xml
	public static final String ACTION_DOWNLOAD_TRACKDATA = "de.playmee.action.DOWNLOAD_TRACKDATA";
	public static final String ACTION_UPLOAD_TRACK = "de.playmee.action.UPLOAD_TRACK";
	
	// notification id
	private final int NOTIFICATION_ID = 1;
	
	// music handler (needed to get data while processing actions)
	private MusicHandler mHandler;
	
	// account handler (needed to upload track data)
	private AccountHandler aHandler;
	
	// handler to handle results of the action
	private Handler handler;
	private Message msg;
	
	/**
	 * called when the service is first created
	 */
	@Override
	public void onCreate() {
		Log.i(TAG, "Creating LoadingService...");
		
		mHandler = ((Playmee) getApplication()).getMusicHandler();
		aHandler = ((Playmee) getApplication()).getAccountHandler();
		
		handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				Result error = Result.getError(msg.arg2);
				Log.i(TAG, error.getMessage());
				stopSelf(msg.arg1);
			}
		};
	}
	
	/**
	 * called when we receive an intent via startService()
	 */
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		final String action = intent.getAction();
				
		// for each start request send a message with the startId
		// with the startId we know which request we're stopping
		msg = handler.obtainMessage();
		msg.arg1 = startId;
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				if (action.equals(ACTION_DOWNLOAD_TRACKDATA)) {
					processDownloadTrackDataRequest();
				} else if (action.equals(ACTION_UPLOAD_TRACK)) {
					processUploadTrackRequest();
				}
			}
		}).start();
		
		// if service gets killed, after returning from here, do not restart
		return START_NOT_STICKY;
	}
	
	/**
	 * loads remaining track data from last.fm
	 */
	private void processDownloadTrackDataRequest() {
		Log.i(TAG, "Downloading remaining data...");
		
		// check internet connection
		Playmee application = (Playmee) getApplication();
		if (!application.hasNetworkConnection())
			return;
		
		if (mHandler.getCheckCount() > 0) {
			setUpAsForeground(NOTIFICATION_ID, "Downloading music data...",
					android.R.drawable.stat_notify_sync);
		
			Result result = Result.Process_Done;
			List<DownloadTrackDataListener> listeners = ((Playmee) getApplication()).getDownloadTrackDataListeners();
		
			while (mHandler.getCheckCount() > 0) {
				Track track = mHandler.getNextTrackFromChecklist();
				Result r = mHandler.completeWithRemaingData(track);
				if (result != Result.Process_DoneWithErrors && r == Result.Process_DoneWithErrors)
					result = Result.Process_DoneWithErrors;
				mHandler.deleteTrackFromChecklist(track.getData());
			
				// notify listeners that a track was updated
				for (DownloadTrackDataListener listener : listeners) {
					listener.onSingleTrackDataDownloaded(track);
				}
			}
				
			// notify listeners that all tracks were updated
			for (DownloadTrackDataListener listener : listeners) {
				listener.onAllTracksDataDownloaded();
			}
		
			msg.arg2 = result.getCode();
			handler.sendMessage(msg);
		
			stopForeground(true);
		}
		
		Log.i(TAG, "Done downloading remaining data.");
	}
	
	/**
	 * uploads track details to our database
	 */
	private void processUploadTrackRequest() {
		Log.i(TAG, "Uploading track details...");
		List<Track> trackErrorUploading = new ArrayList<Track>();
		
		// check internet connection
		Playmee application = (Playmee) getApplication();
		if (!application.hasNetworkConnection())
			return;	
		
		if (mHandler.getUploadCount() > 0) {
			Result result = Result.Process_Done;
			List<UploadTrackDataListener> listeners = ((Playmee) getApplication()).getUploadTrackDataListeners();
			
			while (mHandler.getUploadCount() > 0) {
				Track track = mHandler.getNextTrackFromUploadlist();
				Result r = mHandler.uploadTrack(track, aHandler.getUsername(), aHandler.getPassword());
				
				if (r == Result.Process_Done) {
					track.setStatus(Status.RATED);
					mHandler.updateTrack(track);
				}
				else if (r == Result.Process_DoneWithErrors) {
					// remember that track could not be uploaded
					result = Result.Process_DoneWithErrors;
					trackErrorUploading.add(track);
				}
				
				mHandler.deleteTrackFromUploadlist(track.getData());
				
				// notify listeners that a track was uploaded
				for (UploadTrackDataListener listener : listeners) {
					listener.onSingleTrackDataUploaded(track);
				}
			}
			
			msg.arg2 = result.getCode();
			handler.sendMessage(msg);
			
			// insert tracks that could not be uploaded
			// they will be uploaded the next time we start the service
			for (Track t : trackErrorUploading) {
				mHandler.insertTrackToUploadlist(t.getData(), false);
			}
			
			// notify listeners that all tracks were updated
			for (UploadTrackDataListener listener : listeners) {
				listener.onAllTracksDataUploaded();
			}
		}
		
		Log.i(TAG, "Done uploading data.");
	}
	
	@Override
	public void onDestroy() {
		// service is being killed, so release all resources
		stopForeground(true);
	}

	public interface DownloadTrackDataListener {	
		public void onSingleTrackDataDownloaded(Track track);
		public void onAllTracksDataDownloaded();
	}
	
	public interface UploadTrackDataListener {	
		public void onSingleTrackDataUploaded(Track track);
		public void onAllTracksDataUploaded();
	}
}
